


import AxisLinePrimitive from './AxisLinePrimitive.js'
import TransformAxis from './TransformAxis.js'

let noScale = new Cesium.Cartesian3(1.0, 1.0, 1.0)
let matrixScratch = new Cesium.Matrix4()
let scaleScratch = new Cesium.Cartesian3()
let offsetScratch = new Cesium.Cartesian3()
let widgetOriginScratch = new Cesium.Cartesian3()
let rotationWorldScratch = new Cesium.Cartesian3()
let inverseTransformScratch = new Cesium.Matrix4()
let localStartScratch = new Cesium.Cartesian3()
let localEndScratch = new Cesium.Cartesian3()
let vector1Scratch = new Cesium.Cartesian2();
let vector2Scratch = new Cesium.Cartesian2();
let rayScratch = new Cesium.Ray()
let intersectionScratch = new Cesium.Cartesian3()
let quaternionScratch = new Cesium.Quaternion()
let matrix3Scratch = new Cesium.Matrix3()

export default class RotationEditor {

  constructor(options = {}) {
    let scene = options.scene

    this._vectorLine1 = scene.primitives.add(new AxisLinePrimitive({
      width: 5,
      positions: [new Cesium.Cartesian3(), new Cesium.Cartesian3()],
      color: Cesium.Color.YELLOW,
      show: false
    }))

    this._vectorLine2 = scene.primitives.add(new AxisLinePrimitive({
      width: 5,
      positions: [new Cesium.Cartesian3(), new Cesium.Cartesian3()],
      color: Cesium.Color.YELLOW,
      show: false
    }))

    // 生成圆位置数组
    let circles = getUnitCirclePositions()

    this._polylineX = scene.primitives.add(getLinePrimitive(circles.x, TransformAxis.X));
    this._polylineY = scene.primitives.add(getLinePrimitive(circles.y, TransformAxis.Y));
    this._polylineZ = scene.primitives.add(getLinePrimitive(circles.z, TransformAxis.Z));
    this._modelMatrix = Cesium.Matrix4.clone(Cesium.Matrix4.IDENTITY);

    // 获取原始偏移量
    this.originOffset = options.originOffset || Cesium.Cartesian3.ZERO

    this._scene = scene
    // 设置朝向回调函数
    this._callback = options.callback
    // 设置位置回调函数
    this._setPositionCallback = options.setPosition

    this._transform = options.transform
    // 不理解 半径
    this._radius = options.radius

    // 激活状态
    this._active = false

    this._dragging = false
    this._startTransform = new Cesium.Matrix4()
    this._startRotation = new Cesium.Matrix3()

    this._widgetOrigin = new Cesium.Cartesian3()
    this._modelOrigin = new Cesium.Cartesian3()
    // 要旋转的轴
    this._rotationAxis = undefined

    this._rotationPlane = new Cesium.Plane(Cesium.Cartesian3.UNIT_X, 0.0)
    this._rotationStartPoint = new Cesium.Cartesian3()

    // 鼠标事件
    this._handler = new Cesium.ScreenSpaceEventHandler(scene.canvas)

    this._angle = 0
    this._heading = 0
    this._pitch = 0
    this._roll = 0

    this.update()
  }

  // 更新事件
  bindEvent() {
    this._handler.setInputAction(this.handleLeftDown.bind(this ), Cesium.ScreenSpaceEventType.LEFT_DOWN)
    this._handler.setInputAction(this.handleLeftUp.bind(this), Cesium.ScreenSpaceEventType.LEFT_UP)
    this._handler.setInputAction(this.handleMouseMove.bind(this), Cesium.ScreenSpaceEventType.MOUSE_MOVE)
  }

  /**
   *  解绑事件
   */
  unbindEvent(){
    this._handler.removeInputAction (Cesium.ScreenSpaceEventType.LEFT_DOWN)
    this._handler.removeInputAction (Cesium.ScreenSpaceEventType.LEFT_UP)
    this._handler.removeInputAction (Cesium.ScreenSpaceEventType.MOUSE_MOVE)
  }

  /**
   * 更新
   */
  update() {
    let transform = this._transform
    let modelMatrix = this._modelMatrix
    modelMatrix = Cesium.Matrix4.setScale(transform, new Cesium.Cartesian3(1.0, 1.0, 1.0), modelMatrix);

    let widgetOrigin = getWidgetOrigin(transform, this.originOffset, widgetOriginScratch);
    modelMatrix = Cesium.Matrix4.setTranslation(modelMatrix, widgetOrigin, modelMatrix);

    let radius = this._radius * Cesium.Matrix4.getMaximumScale(this._transform) * 1.25;
    modelMatrix = Cesium.Matrix4.multiplyByUniformScale(modelMatrix, radius, modelMatrix);

    this._polylineX.modelMatrix = modelMatrix;
    this._polylineY.modelMatrix = modelMatrix;
    this._polylineZ.modelMatrix = modelMatrix;
  }

  /**
   * 鼠标按下
   * @param {*} position 
   * @returns 
   */
  handleLeftDown(position) {
    position = position.position
    let scene = this._scene
    let pickedObjects = scene.drillPick(position)
    let pickedAxis
    for (var i = 0; i < pickedObjects.length; i++) {
      var object = pickedObjects[i]
      if (Cesium.defined(object.id) && Cesium.defined(TransformAxis[object.id])) {
        pickedAxis = object.id
        break
      }
    }
    if (!Cesium.defined(pickedAxis)) {
      return
    }

    var rotationAxis = TransformAxis.getValue(pickedAxis)
    var startTransform = Cesium.Matrix4.setScale(this._transform, noScale, this._startTransform)
    this._startRotation = Cesium.Matrix4.getMatrix3(startTransform, this._startRotation)
    var modelOrigin = Cesium.Matrix4.getTranslation(startTransform, this._modelOrigin)

    var widgetOrigin = getWidgetOrigin(this._transform, this.originOffset, this._widgetOrigin)

    var rotationAxisEndWorld = Cesium.Matrix4.multiplyByPoint(startTransform, rotationAxis, rotationWorldScratch)
    var rotationAxisVectorWorld = Cesium.Cartesian3.subtract(rotationAxisEndWorld, modelOrigin, rotationAxisEndWorld)
    rotationAxisVectorWorld = Cesium.Cartesian3.normalize(rotationAxisVectorWorld, rotationAxisVectorWorld)

    var rotationPlane = Cesium.Plane.fromPointNormal(widgetOrigin, rotationAxisVectorWorld, this._rotationPlane)
    var rotationStartPoint = Cesium.IntersectionTests.rayPlane(scene.camera.getPickRay(position, rayScratch), rotationPlane, this._rotationStartPoint)
    this._dragging = Cesium.defined(rotationStartPoint)

    this._rotationAxis = rotationAxis
    scene.screenSpaceCameraController.enableInputs = false
  }

  /**
   * 鼠标拖动
   * @param {*} position 
   * @returns 
   */
  handleMouseMove(position) {
    if (!this._dragging) return
    position = position.endPosition
    var scene = this._scene
    var ray = scene.camera.getPickRay(position, rayScratch)
    var intersection = Cesium.IntersectionTests.rayPlane(ray, this._rotationPlane, intersectionScratch)

    if (!Cesium.defined(intersection)) return

    let widgetOrigin = this._widgetOrigin
    let modelOrigin = this._modelOrigin
    let rotationStartPoint = this._rotationStartPoint

    let vector1 = this._vectorLine1
    let v1Pos = vector1.positions
    let vector2 = this._vectorLine2
    let v2Pos = vector2.positions

    let v1 = Cesium.Cartesian3.subtract(rotationStartPoint, widgetOrigin, vector1Scratch)
    let v2 = Cesium.Cartesian3.subtract(intersection, widgetOrigin, vector2Scratch)
    v2 = Cesium.Cartesian3.normalize(v2, v2)
    v2 = Cesium.Cartesian3.multiplyByScalar(v2, Cesium.Cartesian3.magnitude(v1), v2)
    intersection = Cesium.Cartesian3.add(widgetOrigin, v2, intersection)

    v1Pos[0] = widgetOrigin
    v1Pos[1] = rotationStartPoint
    v2Pos[0] = widgetOrigin
    v2Pos[1] = intersection
    vector1.positions = v1Pos
    vector2.positions = v2Pos
    vector1.show = true
    vector2.show = true

    var offset = Cesium.Cartesian3.multiplyComponents(this.originOffset, Cesium.Matrix4.getScale(this._transform, offsetScratch), offsetScratch)
    var rotationAxis = this._rotationAxis

    // 获取旋转角度
    let angle = getRotationAngle(this._startTransform, offset, rotationAxis, rotationStartPoint, intersection);
    // 获取矩阵
    var rotation = Cesium.Matrix3.fromQuaternion(Cesium.Quaternion.fromAxisAngle(rotationAxis, angle, quaternionScratch), matrix3Scratch)

    if (rotationAxis.x == '1') {
      this._roll = angle
    } else if (rotationAxis.y == '1') {
      this._pitch = angle
    } else {
      this._heading = angle
    }

    let headingPitchRoll = new Cesium.HeadingPitchRoll(this._heading, this._pitch, this._roll)

    // 获取模型缩放
    let modelOriginScale = Cesium.Matrix4.getScale(this._transform, new Cesium.Cartesian3())
    // 根据角度计算矩阵
    let rotationMatrix = Cesium.Matrix4.fromRotationTranslation(
      Cesium.Matrix3.fromHeadingPitchRoll(
        headingPitchRoll
      )
    )
    
    // 创建缩放矩阵
    let scaleMatrix = Cesium.Matrix4.fromScale(modelOriginScale)
    let modelMatrix = new Cesium.Matrix4()

    // 计算模型位置
    var newOffset = Cesium.Cartesian3.negate(offset, vector1Scratch)
    newOffset = Cesium.Matrix3.multiplyByVector(rotation, newOffset, newOffset)
    // 模型位置
    modelOrigin = Cesium.Cartesian3.add(newOffset, widgetOrigin, modelOrigin)
    // 根据原点创建
    let translationMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(modelOrigin)
    // 原始旋转矩阵相乘
    Cesium.Matrix4.multiply(translationMatrix, rotationMatrix, modelMatrix)
    Cesium.Matrix4.multiply(modelMatrix, scaleMatrix, modelMatrix)

    // 轴只更新原始位置 不改变角度
    this._transform = translationMatrix
    this._startTransform = translationMatrix

    // 回调
    this._callback(modelMatrix)
  }

  /**
   * 鼠标抬起 结束编辑
   */
  handleLeftUp() {
    if (!this._dragging) return
    this._dragging = false
    this._vectorLine1.show = false
    this._vectorLine2.show = false
    this._scene.screenSpaceCameraController.enableInputs = true
    this.update()
  }


  isDestroyed() {
    return false
  }


  destroy() {
    this.active = false
    let scene = this._scene
    scene.primitives.remove(this._vectorLine1)
    scene.primitives.remove(this._vectorLine2)
    scene.primitives.remove(this._polylineX)
    scene.primitives.remove(this._polylineY)
    scene.primitives.remove(this._polylineZ)
    Cesium.destroyObject(this)
  }

  get callback(){
    return this._callback
  }

  set callback(val){
    this._callback = val
  }

  get radius() {
    return this._radius
  }

  set radius(val) {
    this._radius = val
    this.update()
  }

  get transform() {
    return this._transform
  }

  set transform(val) {
    this._transform = val
    this.update()
  }

  get active() {
    return this._active
  }


  set active(active) {
    this._active = active
    // 判断是否激活
    if (active) {
      this.bindEvent()
      this._polylineX.show = true
      this._polylineY.show = true
      this._polylineZ.show = true
    } else {
      this.unbindEvent()
      this._polylineX.show = false
      this._polylineY.show = false
      this._polylineZ.show = false
      this._dragging = false
    }

  }
}

function getWidgetOrigin(transform, originOffset, result) {
  var startScale = Cesium.Matrix4.getScale(transform, scaleScratch)
  var modelMatrix = Cesium.Matrix4.setScale(transform, noScale, matrixScratch)
  return Cesium.Matrix4.multiplyByPoint(modelMatrix, Cesium.Cartesian3.multiplyComponents(originOffset, startScale, result), result);
}



function getRotationAngle(transform, originOffset, axis, start, end) {
  var inverseTransform = Cesium.Matrix4.inverse(transform, inverseTransformScratch)
  var localStart = Cesium.Matrix4.multiplyByPoint(inverseTransform, start, localStartScratch)//project points to local coordinates so we can project to 2D
  var localEnd = Cesium.Matrix4.multiplyByPoint(inverseTransform, end, localEndScratch)

  localStart = Cesium.Cartesian3.subtract(localStart, originOffset, localStart)
  localEnd = Cesium.Cartesian3.subtract(localEnd, originOffset, localEnd)

  var v1 = vector1Scratch;
  var v2 = vector2Scratch;
  if (axis.x) {
    v1.x = localStart.y;
    v1.y = localStart.z;
    v2.x = localEnd.y;
    v2.y = localEnd.z;
  } else if (axis.y) {
    v1.x = -localStart.x;
    v1.y = localStart.z;
    v2.x = -localEnd.x;
    v2.y = localEnd.z;
  } else {
    v1.x = localStart.x;
    v1.y = localStart.y;
    v2.x = localEnd.x;
    v2.y = localEnd.y;
  }
  var ccw = v1.x * v2.y - v1.y * v2.x >= 0.0; //true when minimal angle between start and end is a counter clockwise rotation
  var angle = Cesium.Cartesian2.angleBetween(v1, v2);
  if (!ccw) {
    angle = -angle;
  }
  return angle;
}


// 生成圆
function getUnitCirclePositions() {
  var xAxis = []
  var yAxis = []
  var zAxis = []

  for (var i = 0; i < 360; i++) {
    var rad = Cesium.Math.toRadians(i)
    var x = Math.cos(rad)
    var y = Math.sin(rad)

    xAxis.push(new Cesium.Cartesian3(0.0, x, y))
    yAxis.push(new Cesium.Cartesian3(y, 0.0, x))
    zAxis.push(new Cesium.Cartesian3(x, y, 0.0))
  }
  return {
    x: xAxis,
    y: yAxis,
    z: zAxis
  }
}

// 生成轴线
function getLinePrimitive(positions, axis) {
  return new AxisLinePrimitive({
    positions: positions,
    color: TransformAxis.getColor(axis),
    loop: true,
    show: false,
    id: axis
  })
}